#include <cbm.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <conio.h>
//#include <stdbool.h>

#include "Dispatcher.h"
#include "Directory.h"
#include "Buffers.h"
#include "Trim.h"
#include "Splitter.h"
#include "cbm_dir.h"
#include "Drives.h"

#pragma code-name("COPYCODE");
#pragma rodata-name("COPYCODE");

// Convert a file-type code-number into the type's initial letter.
// d - DELeted, but closed
// S - SEQuential
// P - PRoGram
// U - USeR-made format
// R - RELative
// C - CBM partition
// D - DIRectory
// L - soft-LiNK
// V - VoRPal fast-loading
// O - any Other type
char __fastcall__ getFileType(unsigned char type)
{
	if(type < CBM_T_OTHER) return "dSPURCDLV"[type];
	return 'O';
}

void closeFiles(void)
{
	//char i = 2;
	//for(; i < 16; ++i)
	//{
	//	if(i != 4 && i != 6 && i != 7)
	//	{
	//		cbm_close(i);
	//	}
	//}

	cbm_close(2);
	cbm_close(3);
}

unsigned char copyMoveFile(char *name, char fileType, parsedPath_t *source, parsedPath_t *target, unsigned char move, tokenCollection_t *collection, unsigned int blocks)
{
	int r = 0, b = 0, w = 0;
	int x, y;
	drive_t *sdrive = &drives[getDrive(source->device)];
	drive_t *tdrive = &drives[getDrive(target->device)];
	
	if(source->path[0] != 0 && sdrive->type != CBM15x1)
	{
		if(sdrive->type == VICE)
		{
			sprintf(buffer2, "cd:%s", source->path);
			strcpy(sdrive->path, source->path);
			executeCommand(sdrive, buffer2, 0);
		}
		else
		{
			sprintf(buffer2, "cd%s", source->path);
			strcpy(sdrive->path, source->path);
			executeCommand(sdrive, buffer2, 0);
		}
	}

	printf("\n%sing %s", collection->tokens[0].text, name);
	x = 0;
	y = wherey() + 1;
	memset(buffer2, 0, sizeof(buffer2));
	sprintf(buffer2, "%s,%c,r", name, fileType);
	//printf("\nsource: %s", buffer2);
	cbm_close(2);
	r = cbm_open(2, source->device, 2, buffer2);
	if(r == 0)
	{
		if(target->path[0] != 0 && tdrive->type != CBM15x1)
		{
			if(tdrive->type == VICE)
			{
				sprintf(buffer2, "cd:%s", target->path);
				executeCommand(tdrive, buffer2, 0);
			}
			else
			{
				sprintf(buffer2, "cd%s", target->path);
				executeCommand(tdrive, buffer2, 0);
			}
		}

		if(strlen(target->file) == 0) strcpy(target->file, name);

		memset(buffer2, 0, sizeof(buffer2));
		sprintf(buffer2, "%s,%c,w", target->file, fileType);
		//printf("\ntarget: %s", buffer2);
		cbm_close(3);
		r = cbm_open(3, target->device, 3, buffer2);
		if(r == 0 || r == 2)
		{
			while(r==2 && strlen(target->file) < 16)
			{
				cbm_close(3);
				strcat(target->file, "+");
				memset(buffer2, 0, sizeof(buffer2));
				sprintf(buffer2, "%s,%c,w", target->file, fileType);
				//printf("\ntarget: %s", buffer2);
				r = cbm_open(3, target->device, 3, buffer2);
			}

			if(r == 0)
			{
				x = 0;
				y = wherey()+1;
				if(y == 25)
				{
					scrolldown();
					y = 24;
				}
				while((r = cbm_read(2, buffer3, 254)) > 0 && b < blocks)
				{
					if(kbhit() && cgetc() == CH_STOP) 
					{
						closeFiles();
						return 1;
					}
					gotoxy(x, y);
					cprintf("%s [%u/%u:%3u]", name, ++b, blocks, r);
					w = cbm_write(3, buffer3, r);
				}
				//printf("\n[r: %d, w: %d] before close...", r, w);

				closeFiles();

				//printf("\nafter close...");

				if(move > 0)
				{
					sprintf(buffer2, "s%u:%s", source->drive, name);
					executeCommand(sdrive, buffer2, 0);
				}
			}
			else
			{
				closeFiles();
				printf("\nERROR opening %s: %d", target->file, r);
				return r;
			}
		}
		else
		{
			closeFiles();
			printf("\nERROR opening %s: %d", target->file, r);
			return r;
		}
	}
	else
	{
		closeFiles();
		printf("\nERROR opening source: %d", r);
		return r;
	}

	closeFiles();
	return 0;
}

unsigned char copyMove(unsigned char driveIndex)
{
	bool complete = false;
	int r = 0, c = 0, count = 0, p = 0;
	int compare;
	struct cbm_dirent entry;
	char showSize = 1;
	drive_t *drive = &drives[driveIndex];
	tokenCollection_t *collection = NULL;
	static char buffer[256], last[17];
	parsedPath_t *source;
	parsedPath_t *target;

	strcpy(buffer, buffer3);

	collection = splitString(buffer);

	if(collection->count < 2 || collection->count > 3)
	{
		printf("\nusage: %s source [target]", collection->tokens[0].text);
		free(source);
		free(target);
		free(collection);
		return 0;
	}

	if(strcmp(collection->tokens[0].text, "move") == 0)
	{
		collection->tokens[0].text[3] = 0;

	}

	source = parsePath(1, collection, driveIndex);

	if((driveIndex = getDrive(source->device)) == 0xFF)
	{
		printf("\nUnknown source device.");
		free(collection);
		free(source);
		return 1;
	}

	if(collection->count == 3)
	{
		target = parsePath(2, collection, driveIndex);

		if(getDrive(target->device) == 0xFF)
		{
			printf("\nUnknown target device.");
			free(collection);
			free(source);
			free(target);
			return 1;
		}
	}
	else
	{
		target = (parsedPath_t *)malloc(sizeof(parsedPath_t));
		target->device = drive->device;
		target->drive = drive->drive;
		strcpy(target->path, drive->path);
		memset(target->file, 0, 16);
	}
	
	if(strlen(source->file) == 0 && 
		strcmp(source->path, drive->path) == 0)
	{
		printf("\nSource does not specify a file or pattern.");
		free(source);
		free(target);
		free(collection);
		return 0;
	}
	
	if(
	   (
		target->device == source->device &&
		target->drive == source->drive
	   ) && (
		(
			strlen(target->path) == 0 &&
			strlen(target->file) == 0 &&
			strcmp(source->path, target->path)
		) ||
		(
			strcmp(target->path, source->path) == 0	&& 
			strcmp(target->file, source->file) == 0
		)
	   )
	)
	{
		printf("\nCannot copy file to itself.");
		free(source);
		free(target);
		free(collection);
		return 0;
	}

	if(isPattern(source->path) || isPattern(target->path))
	{
		printf("\nPaths cannot contain patterns.");
		free(source);
		free(target);
		free(collection);
		return 0;
	}

	if(isPattern(target->file))
	{
		printf("\nTarget filename cannot contain patterns.");
		free(source);
		free(target);
		free(collection);
		return 0;
	}


	if(isPattern(source->file))
	{
		if(0) cgetc();
//printf("\nCOPY BATCH ..a.."); cgetc();

		memset(buffer2, 0, sizeof(buffer2));

//printf("\nCOPY BATCH ..b.."); cgetc();
		if(strlen(source->path) > 0)
		{
			sprintf(buffer2, (drives[getDrive(source->device)].type == VICE ? "cd:%s" : "cd%s"), source->path);
			strcpy(drives[getDrive(source->device)].path, source->path);
//printf("\nCOPY BATCH ..c.."); cgetc();
			if(!executeCommand(&drives[getDrive(source->device)], buffer2, 0))
			{
				printf("\nInvalid source path.");
				cbm_close(5);
				free(source);
				free(target);
				free(collection);
				return 1;
			}
		}

//printf("\nCOPY BATCH ..d.."); cgetc();
		if(strcmp(source->file, "*") == 0)
		{
			sprintf(buffer2, "$%1u", source->drive);
		}
		else
		{
//printf("\nCOPY BATCH ..e.."); cgetc();
			if(drives[getDrive(source->device)].type == CBM15x1)
			{
				sprintf(buffer2, "$%1u:%s", source->drive, source->file);
			}
			else if(drives[getDrive(source->device)].type == VICE)
			{
				sprintf(buffer2, "$%1u:%s/%s", source->drive, source->path, source->file);
			}
			else
			{
				sprintf(buffer2, "$%1u%s:%s", source->drive, source->path, source->file);
			}
		}

//printf("\nCOPY BATCH ..f.."); cgetc();
		printf("\n%s", buffer2);
		r = cbm_opendir2(6, source->device, buffer2);

//printf("\nCOPY BATCH ..g.."); cgetc();
		if(r == 0)
		{
//printf("\nCOPY BATCH ..h.."); cgetc();
			r = cbm_open(10, target->device, 3, "copylist,s,w");

//printf("\nCOPY BATCH ..i.."); cgetc();
			if(r == 0)
			{
				// Header
//printf("\nCOPY BATCH ..j.."); cgetc();
				r = cbm_readdir(6, &entry);

//printf("\nCOPY BATCH ..k.."); cgetc();
				if(r == 0)
				{
					memset(&entry, 0, sizeof(entry));
					while(cbm_readdir(6, &entry) != 2)
					{
						if(getFileType(entry.type) != 'R' && getFileType(entry.type) != 'D' && getFileType(entry.type) != 'd')
						{
							cbm_write(10, &entry, sizeof(entry));
							++count;
						}
						else
						{
							printf("\nSkipping %s because type = %c", entry.name, getFileType(entry.type));
						}
						memset(&entry, 0, sizeof(entry));
					}

					//printf("\nCOPYing %u files.", count);

					cbm_close(10);

					memset(last, 0, sizeof(last));
					memset(buffer1, 0, sizeof(buffer1));
					memset(&entry, 0, sizeof(entry));
					while(!complete)
					{
						r = cbm_open(10, target->device, 0, "copylist,s,r");

						while((r = cbm_read(10, &entry, sizeof(entry))) != 0)
						{
							if(last[0] != 0)
							{
								if(strcmp(last, entry.name) == 0)
								{
									r = cbm_read(10, &entry, sizeof(entry));
									break;
								}
							}
							else
							{
								break;
							}
							memset(&entry, 0, sizeof(entry));
						}

						cbm_close(10);

						

						if(r != 0)
						{
							r = copyMoveFile(entry.name, tolower(getFileType(entry.type)), source, target, collection->tokens[0].text[0] == 'm', collection, entry.size);
							if(r > 0) 
							{
								printf("\nAborted [%u]", r);
								executeCommand(&drives[getDrive(target->device)], "s0:copylist", 0);
								break;
							}
							memset(target->file, 0, sizeof(target->file));
							strcpy(last, entry.name);
						}
						else
						{
							printf("\nBatch copy completed.");
							complete = true;
							executeCommand(&drives[getDrive(target->device)], "s0:copylist", 0);
						}
					}
				}
				else
				{
					printf("\nCould not read directory.");
				}
			}
			else
			{
				cbm_close(10);
				printf("\nCould not write file list.");
			}
		}
		else
		{
			printf("\nCould not open source directory.");
			cbm_close(6);
			free(source);
			free(target);
			free(collection);
			return r;
		}
		r = 0;
	}
	else
	{
		if(0) cgetc();
//printf("\n..0.."); cgetc();
		if(drives[getDrive(source->device)].type != CBM15x1)
		{
			if(drives[getDrive(source->device)].type == VICE)
			{
				sprintf(arguments, "cd:%s", source->path);
//printf("\n..a.."); cgetc();
				executeCommand(&drives[getDrive(source->device)], arguments, 0);
//printf("\n..b.."); cgetc();
			}
			else
			{
				sprintf(arguments, "cd%s", source->path);
//printf("\n..c.."); cgetc();
				executeCommand(&drives[getDrive(source->device)], arguments, 0);
//printf("\n..d.."); cgetc();
			}
		}
//printf("\n..e.."); cgetc();
		r = cbm_opendir2(1, source->device, "$");
//printf("\n..f.."); cgetc();
		if(r == 0)
		{
//printf("\n..g.."); cgetc();
			cbm_readdir(1, &entry);
//printf("\n..h.."); cgetc();
			//printf("\nSearching for [%s]", source->file);
			while((compare = strcmp(entry.name, source->file)) != 0 && (r = cbm_readdir(1, &entry)) != 2);

//printf("\n..i.."); cgetc();
			cbm_close(1);
//printf("\n..j.."); cgetc();

			if(compare == 0)
			{
//printf("\n..k.."); cgetc();
				r = copyMoveFile(source->file, tolower(getFileType(entry.type)), source, target, collection->tokens[0].text[0] == 'm', collection, entry.size);
				if(r != 0) r = 1;
//printf("\n..l.."); cgetc();
			}
			else
			{
				printf("\nFile not found.");
			}
		}
		else
		{
			printf("\nError %d opening directory.");
		}
	}

	cbm_close(6);
	free(source);
	free(target);
	free(collection);

	//while(1);
//printf("\nCOPY/Move complete....");
//cgetc();

	return r;
}